package cn.cocho.android.mc.comp.anim;

import java.util.Random;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

import cn.cocho.android.mc.comp.util.CompUtils;

public class RippleView extends View implements Runnable {

	boolean m_isRunning = false;
	boolean m_isRain = false;

	int m_width;
	int m_height;

	/*
	 * 建立两个与水池图象一样大小的数组 buf1[PoolWidth * PoolHeight]和 buf2[PoolWidth *
	 * PoolHeight](PoolWidth 为水池图象的象素宽度、PoolHeight 为水池图象
	 * 的象素高度),用来保存水面上每一个点的前一时刻和后一时刻波幅数据,因为波幅也就代
	 * 表了波的能量,所以在后面我们称这两个数组为波能缓冲区。水面在初始状态时是一个平面, 各点的波幅都为 0,所以,这两个数组的初始值都等于 0
	 */
	short[] m_buf1;
	short[] m_buf2;

	int[] m_bitmap1;
	int[] m_bitmap2;

	Thread m_thread;

	int m_preX;
	int m_preY;

	Random random;

	public RippleView(Context context) {
		super(context);
		initRippleView();
	}
	
	public RippleView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initRippleView();
	}
	
	public void initRippleView(){
		random = new Random();
		Drawable drawable = this.getBackground();
		BitmapDrawable bd = (BitmapDrawable) drawable;
		Bitmap image = bd.getBitmap();
		
		m_width = image.getWidth();
		m_height = image.getHeight();
//		m_width = 200;
//		m_width = DensityUtils.getScreenWidth();
//		m_height = 300;

		// leave 1 extra up, low border for boundary condition
		m_buf1 = new short[m_width * (m_height)];
		m_buf2 = new short[m_width * (m_height)];

		m_bitmap1 = new int[m_width * m_height];
		m_bitmap2 = new int[m_width * m_height];

		image.getPixels(m_bitmap1, 0, m_width, 0, 0, m_width, m_height);
		
		start();
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawBitmap(m_bitmap2, 0, m_width, 0, 0, m_width, m_height, false, null);
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		int action = event.getAction();

		if (KeyEvent.ACTION_DOWN == action) {
			switch (keyCode) {
			case KeyEvent.KEYCODE_0:
				m_isRain = !m_isRain;
				break;
			}
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
//		super.onTouchEvent(event);
		int action = event.getAction();
		int x = (int) (event.getX());
		int y = (int) (event.getY());

		switch (action) {
		case MotionEvent.ACTION_DOWN:
			dropStone(x, y, 8, 50);
			m_preX = x;
			m_preY = y;
			break;

		case MotionEvent.ACTION_MOVE:
			bresenhamDrop(m_preX, m_preY, x, y, 4, 40);
			m_preX = x;
			m_preY = y;
			break;
		case MotionEvent.ACTION_UP:
			if (CompUtils.isClick(event, this)) {
				performClick();// 如果不加该方法，则在为该按钮添加点击事件时，点击事件失效
			}
		}
		return true;
	}

	public void start() {
		m_isRunning = true;
		m_thread = new Thread(this);
		m_thread.start();
	}

	public void stop() {
		m_isRunning = false;
	}

	public void resume() {
		m_isRunning = true;
	}

	public void destroy() {
		stop();
		m_thread.interrupt();
	}

	public void run() {
		while (m_isRunning) {
			if (m_isRain) {
				int x = 10 + random.nextInt() % (m_width - 20);
				int y = 10 + random.nextInt() % (m_height - 20);
				dropStone(x, y, 3, 80);
			}

			rippleSpread();

			rippleRender();

			postInvalidate();
		}
	}

	// 某点下一时刻的波幅算法为：上下左右四点的波幅和的一半减去当前波幅，即
	// X0' =（X1 + X2 + X3 + X4）/ 2 - X0
	// +----x3----+
	// + | +
	// + | +
	// x1---x0----x2
	// + | +
	// + | +
	// +----x4----+
	//
	/**
	 * 计算波幅数据
	 * 
	 * @author HAIKANG SONG
	 */
	void rippleSpread() {
		int pixels = m_width * (m_height - 1);
		for (int i = m_width; i < pixels; ++i) {
			// 波能扩散:上下左右四点的波幅和的一半减去当前波幅
			// X0' =（X1 + X2 + X3 + X4）/ 2 - X0
			//
			m_buf2[i] = (short) (((m_buf1[i - 1] + m_buf1[i + 1] + m_buf1[i - m_width] + m_buf1[i + m_width]) >> 1) - m_buf2[i]);

			// 波能衰减 1/32
			//
			m_buf2[i] -= m_buf2[i] >> 5;
		}

		// 交换波能数据缓冲区
		short[] temp = m_buf1;
		m_buf1 = m_buf2;
		m_buf2 = temp;
	}

	/**
	 * 根据算出的波幅数据对页面进行渲染
	 * 
	 * @author HAIKANG SONG
	 */
	void rippleRender() {
		int offset;
		int i = m_width;
		int length = m_width * m_height;
		for (int y = 1; y < m_height - 1; ++y) {
			for (int x = 0; x < m_width; ++x, ++i) {
				// 计算出偏移象素和原始象素的内存地址偏移量 : offset = width * yoffset + xoffset
				offset = (m_width * (m_buf1[i - m_width] - m_buf1[i + m_width])) + (m_buf1[i - 1] - m_buf1[i + 1]);

				// 判断坐标是否在窗口范围内
				if (i + offset > 0 && i + offset < length) {
					m_bitmap2[i] = m_bitmap1[i + offset];
				} else {
					m_bitmap2[i] = m_bitmap1[i];
				}
			}
		}
	}

	/**
	 * 触发单个波纹（投入石头时触发的波纹）
	 * 
	 * 为了形成水波，我们必须在水池中加入波源，你可以想象成向水中投入石头，<br>
	 * 形成的波源的大小和能量与石头的半径和扔石头的力量都有关系。<br>
	 * 我们只要修改波能数据缓冲区 buf，让它在石头入水的地点来一个负的"尖脉冲"，<br>
	 * 即让 buf[x, y] = -n。经过实验，n 的范围在（32 ~ 128）之间比较合适。<br>
	 * 
	 * @param x
	 * @param y
	 * @param stoneSize
	 *            波源半径
	 * @param stoneWeight
	 *            波源能量
	 * @author HAIKANG SONG
	 */
	void dropStone(int x, int y, int stoneSize, int stoneWeight) {
		// 判断坐标是否在屏幕范围内
		if ((x + stoneSize) > m_width || (y + stoneSize) > m_height || (x - stoneSize) < 0 || (y - stoneSize) < 0) {
			return;
		}

		int value = stoneSize * stoneSize;
		short weight = (short) -stoneWeight;
		for (int posx = x - stoneSize; posx < x + stoneSize; ++posx) {
			for (int posy = y - stoneSize; posy < y + stoneSize; ++posy) {
				if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < value) {
					m_buf1[m_width * posy + posx] = weight;
				}
			}
		}

		resume();
	}

	/**
	 * 处理一行波纹
	 * 
	 * @param x
	 * @param y
	 * @param stoneSize
	 * @param stoneWeight
	 * @author HAIKANG SONG
	 */
	void dropStoneLine(int x, int y, int stoneSize, int stoneWeight) {
		// 判断坐标是否在屏幕范围内
		if ((x + stoneSize) > m_width || (y + stoneSize) > m_height || (x - stoneSize) < 0 || (y - stoneSize) < 0) {
			return;
		}

		for (int posx = x - stoneSize; posx < x + stoneSize; ++posx) {
			for (int posy = y - stoneSize; posy < y + stoneSize; ++posy) {
				m_buf1[m_width * posy + posx] = -40;
			}
		}

		resume();
	}

	/**
	 * 滑动时触发
	 * 
	 * @param xs
	 * @param ys
	 * @param xe
	 * @param ye
	 * @param size
	 * @param weight
	 * @author HAIKANG SONG
	 */
	// xs, ys : 起始点，xe, ye : 终止点，size : 波源半径，weight : 波源能量
	void bresenhamDrop(int xs, int ys, int xe, int ye, int size, int weight) {
		int dx = xe - xs;
		int dy = ye - ys;
		dx = (dx >= 0) ? dx : -dx;
		dy = (dy >= 0) ? dy : -dy;

		if (dx == 0 && dy == 0) {
			dropStoneLine(xs, ys, size, weight);
		} else if (dx == 0) {
			int yinc = (ye - ys != 0) ? 1 : -1;
			for (int i = 0; i < dy; ++i) {
				dropStoneLine(xs, ys, size, weight);
				ys += yinc;
			}
		} else if (dy == 0) {
			int xinc = (xe - xs != 0) ? 1 : -1;
			for (int i = 0; i < dx; ++i) {
				dropStoneLine(xs, ys, size, weight);
				xs += xinc;
			}
		} else if (dx > dy) {
			int p = (dy << 1) - dx;
			int inc1 = (dy << 1);
			int inc2 = ((dy - dx) << 1);
			int xinc = (xe - xs != 0) ? 1 : -1;
			int yinc = (ye - ys != 0) ? 1 : -1;

			for (int i = 0; i < dx; ++i) {
				dropStoneLine(xs, ys, size, weight);
				xs += xinc;
				if (p < 0) {
					p += inc1;
				} else {
					ys += yinc;
					p += inc2;
				}
			}
		} else {
			int p = (dx << 1) - dy;
			int inc1 = (dx << 1);
			int inc2 = ((dx - dy) << 1);
			int xinc = (xe - xs != 0) ? 1 : -1;
			int yinc = (ye - ys != 0) ? 1 : -1;

			for (int i = 0; i < dy; ++i) {
				dropStoneLine(xs, ys, size, weight);
				ys += yinc;
				if (p < 0) {
					p += inc1;
				} else {
					xs += xinc;
					p += inc2;
				}
			}
		}

		resume();
	}
}
